package org.tru42.signal.model;

import java.lang.reflect.InvocationTargetException;
import java.util.LinkedList;
import java.util.List;

import org.tru42.signal.lang.ConnectException;
import org.tru42.signal.lang.ISignal;
import org.tru42.signal.lang.ISink;

public abstract class AbstractSignal implements ISignal {

	protected final Object owner;
	public final List<ISink> sinks = new LinkedList<ISink>();
	protected Object[] value;
	protected long timestamp;

	public AbstractSignal(Object owner) {
		this.owner = owner;
	}

	@Override
	public boolean compatible(Object obj) {
		if(obj instanceof Sink)
			return ((Sink)obj).compatible(this);
		else
			return false;
	}
	
	@Override
	public void connect(Object obj) throws ConnectException {
		try {
			connectSink((ISink) obj);
			((ISink)obj).connectSignal(this);
		} catch (Exception e) {
			throw new ConnectException(e.getMessage());
		}
	}
	
	public void connectSink(ISink sink) throws ConnectException {
		if(!sink.compatible(this))
			throw new ConnectException("Incompatible sink");
		synchronized (sinks) {
			this.sinks.add(sink);
		}
	}

	public void disconnectSink(ISink sink) {
		if(sink != null)
			synchronized (sinks) {
				this.sinks.remove(sink);
			}
	}

	public void disconnectSinks() {
		synchronized (sinks) {
			this.sinks.clear();
		}
	}

	public void disconnect() {
		
	}

	@Override
	public boolean isConnected() {
		return sinks.size() > 0;
	}
	
	public Object getOwner() {
		return owner;
	}

	public long getTimestamp() {
		return timestamp;
	}

	public Object[] getValue() {
		return value;
	}

	public Sink[] getConnectedSinks() {
		return sinks.toArray(new Sink[sinks.size()]);
	}

	@Override
	public void setValue(Object... args) {
		setValue(System.currentTimeMillis(), args);
	}

	public void setValue(long t, Object... args) {
		this.value = args;
		this.timestamp = t;
		synchronized (sinks) {
			for(ISink sink : sinks)
				try {
					sink.signalValueChanged(this.timestamp, args);
				} catch (Exception e) {
					e.printStackTrace();
				}
		}
	}


	public abstract void trigger() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
	
	public abstract String getName();

	public abstract Class<?>[] getTypes();

}
